package fr.umlv.ig.metroView.map;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JComponent;

import fr.umlv.ig.listenerFactory.MetroMapTrainsListenerFactory;
import fr.umlv.ig.model.MetroModel;
import fr.umlv.ig.model.MetroModelImpl;
import fr.umlv.ig.model.Train;


/**
 * The Map which represents the trains. It's a JComponent. Each Train of the 
 * model are represented by a component {@link JTrain}.
 * 
 * User click are capted by the JTrain.
 * 
 * In this map you can select and or remove JTrain.
 * 
 * In this view only one train can be selected.
 * 
 * @author jenseric
 * @author william
 * 
 *
 */
@SuppressWarnings("serial")
public class MetroMapTrains extends JComponent {
	/**
	 * The {@link MetroModelImpl} used to draw the stations.
	 */	
	private final MetroModelImpl metroModel;
	
	/**
	 * the currentSelected Station.
	 */
	private Train currentSelectedTrain;

	/**
	 * Map where each train are associated by a JTrain.
	 */
	private final Map<Train, JTrain> mapTrains = new HashMap<Train, JTrain>();
	public AffineTransform tranform = new AffineTransform();

	
	/**
	 * The current Scale of the image.
	 */
	private double scale;
	
	/**
	 * The current translate x of the image when scale = 1;
	 */
	private double translateX ;
	
	/**
	 * The current translate y of the image when scale =1;
	 */
	private double translateY;
	

	/**
	 * Initializes a {@link MetroMapTrains}.
	 * @param scale the scale of the map.
	 * @param translateX the translation in x when the scale is 1.
	 * @param translateY the translation on y when the scale is 1.
	 * @param metroModel the {@link MetroModel} concerned by JTrain of the 
	 *        component.
	 */
	public MetroMapTrains(double scale, double translateX, double translateY, final MetroModelImpl metroModel) {
		this.scale = scale;
		this.translateX = translateX;
		this.translateY = translateY;
		this.metroModel = metroModel;

		metroModel.addTrainListener(MetroMapTrainsListenerFactory.getTrainListener(this));
		metroModel.addTrainsListener(MetroMapTrainsListenerFactory.getTrainsListener(this));


	}


	/**
	 * Calls when a zoom occurred.
	 * @param e the {@link MouseWheelEvent} which performed the zoom.
	 * @param scale the scale when we enter in the method.
	 * @param translateX the translate in x when the scale is 1 and we enter 
	 *        in the method.
	 * @param translateY the translate in y when the scale is 1 and we enter 
	 *        in the method.
	*/
	public void zoom(MouseWheelEvent e, double scale, double translateX, double translateY) {
		this.scale = scale;
		this.translateX = translateX;
		this.translateY = translateY;
		drawWithTransform();
	}

	/**
	 * Draw each elements of the component with 
	 * the transformations.
	 */
	public void drawWithTransform() {
		for(Component c : getComponents()) {
			JTrain jtrain = (JTrain) c;
			Point2D p = metroModel.getCoordinate(jtrain.getTrain());
			
			jtrain.setPreferredSize(new Dimension((int) (10.0*scale),(int) (10.0* scale)));
			double realTranslateX = (getWidth() - getWidth()/scale) / 2;
			double realTranslateY = (getHeight() - getHeight()/scale) / 2;

			double x = (p.getX() - realTranslateX) * scale + translateX*scale - 2*scale;
			double y = (p.getY() - realTranslateY) * scale + translateY*scale - 2*scale;

			jtrain.setBounds((int)(x),(int) (y),(int) (10.0 *scale),(int) (10.0 *scale));
			jtrain.setDataTransforms(scale);
		}
	}

	/**
	 * Adds a JTrain to the component. Calls when a train
	 * is added to the model.
	 * @param train the train which is associated by the JTrain added.
	 */
	public void addTrain(Train train) {
		JTrain jtrain = new JTrain(train);
		mapTrains.put(train, jtrain);
		Point2D p = metroModel.getCoordinate(train);
		jtrain.setLocation((int)p.getX()-2,(int) p.getY()-2);
		jtrain.setPreferredSize(new Dimension(10, 10));
		add(jtrain);
	}

	/**
	 * Removes a JTrain. Calls when a train is remove from
	 * the model.
	 * @param train train which is associated by the jTrain.
	 */
	public void removeTrain(Train train) {
		JTrain jtrain = mapTrains.remove(train);
		remove(jtrain);
		repaint();
	}

	/**
	 * Calls when a train on the model is selected.
	 * @param train the train selected.
	 */
	public void selectTrain(Train train) {
		if(train.equals(currentSelectedTrain)) {
			return;
		}
		if(currentSelectedTrain != null) {
			metroModel.unselectedTrain(currentSelectedTrain);
			
		}
		currentSelectedTrain = train;
		JTrain jTrain = mapTrains.get(train);
		jTrain.setColor(Color.GREEN);
		jTrain.repaint();
	}
	
	/**
	 * Calls when a Train is unselect from the model.
	 * @param train the train unselected.
	 */
	public void unSelectTrain(Train train) {
		if(train.equals(currentSelectedTrain)) {
			currentSelectedTrain = null;
		}
		JTrain jTrain = mapTrains.get(train);
		if(jTrain==null) {
			return;
		}
		jTrain.setColor(Color.WHITE);
		jTrain.repaint();
	}
	
	/**
	 * Calls when a translation occurred.
	 * @param e the {@link MouseEvent} which performed the zoom.
	 * @param scale the scale when we enter in the method.
	 * @param translateX the translate in x when the scale is 1 and we enter 
	 *        in the method.
	 * @param translateY the translate in y when the scale is 1 and we enter 
	 *        in the method.
	*/
	public void translate(MouseEvent e, double scale, double x, double y) {
		this.scale = scale;
		this.translateX = x;
		this.translateY = y;
		drawWithTransform();
	}

	
	/**
	 * Calls when a JTrain is selected.
	 * Prevents the model that the train associated by the JTrain is selected.
	 * @param train the train selected.
	 */
	public void giveSelectedTrain(Train train) {
		if(train.equals(currentSelectedTrain)) {
			return;
		}
		if(currentSelectedTrain!=null) {
			metroModel.unselectedTrain(currentSelectedTrain);
		}
		metroModel.selectedTrain(train);
	}
	
	
	/**
	 * Calls when a JTrain is selected by a right click.
	 * Prevents the model that the train the view want to remove a train.
	 * @param train the train  to remove.
	 */
	public void giveRemovedTrain(Train train) {
		if(currentSelectedTrain != null) {
			metroModel.unselectedTrain(currentSelectedTrain);
		}
		if(!mapTrains.containsKey(train)) {
			return;
		}
		metroModel.removeTrain(train);

	}


}
